package com.vecspace.web.servlet.model;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.vecspace.cache.InMemoryCache;
import com.vecspace.core.support.auth.BaseAuthManager;
import com.vecspace.data.model.reflect.ArgumentSimpleInfo;
import com.vecspace.data.model.reflect.BeanSchema;
import com.vecspace.data.model.reflect.ClassSimpleInfo;
import com.vecspace.data.model.reflect.MethodSimpleInfo;
import com.vecspace.exception.HException;
import com.vecspace.utils.Convert;

/**
 * @类型名称: BeanInvoker
 * @version: 1.0
 * @author: wanghua
 */
public class BeanInvoker {
	private static final String CLASS_INFO_CACHE_NAME = BeanInvoker.class.getName();
	/**执行method，并返回执行结果
	 * 函数的参数值从request中获取
	 * 函数的参数名由@Param注解定义，与request中的key一致
	 * @param beanName spring bean name
	 * @param methodPathName method上@Path注解中定义的名称，根据此名称查找method
	 * @param request
	 * @param response
	 * @return
	 * @throws HException
	 * @throws Exception 
	 * @author: wanghua
	 */
	public static Object invokeMethod(Object handler, Method method, HttpServletRequest request, HttpServletResponse response) throws HException, Exception{
		if(handler == null) {
			throw new HException("2011", "BaseRestServlet:handler is null");
		}
		if(method != null){
			
			////进行权限判断 START
			BaseAuthManager.checkAuthorization(method);
			////权限判断 END
					
			Map<String,String> parameterMap = new HashMap<>();
			Enumeration<?> enums = request.getParameterNames();
			while(enums.hasMoreElements()){
				String key = enums.nextElement().toString();
				String val = request.getParameter(key);		 
				parameterMap.put(key, val); 
			}
			
			Class<?>[] parameterTypes = method.getParameterTypes();
			Type[] genericParameterTypes = method.getGenericParameterTypes();

			Annotation[][] parameterAnnotations = method.getParameterAnnotations();
			Object[] paramValues = new Object[parameterAnnotations.length];
			for (int j = 0; j < parameterAnnotations.length; j++)// 遍历所有的参数
			{
				Object pvalue;
				if(ServletRequest.class.isAssignableFrom(parameterTypes[j])){
					pvalue = request;
				}else if(ServletResponse.class.isAssignableFrom(parameterTypes[j])){						
					pvalue = response;
				}else{
					String paramStrValue = null;
					Annotation[] paramAnnotations = parameterAnnotations[j];
					int length = paramAnnotations.length;
					boolean findParam = false;
					for (int k = 0; k < length; k++) {						
						// 获得参数注释
						Annotation paramAnnotation = paramAnnotations[k];
						if (paramAnnotation.annotationType().equals(Param.class)) {
							findParam = true;
							Param param = (Param) paramAnnotation;
							String paramName = param.value();
							paramStrValue = parameterMap.get(paramName);
						}
					}
					if(!findParam) {
						throw new Exception(handler.getClass().getSimpleName()+"."+method.getName()+"函数的参数没有定义Param注解");
					}
					pvalue = Convert.toObject(paramStrValue, parameterTypes[j], genericParameterTypes[j]);
				}
				paramValues[j] = pvalue;
			}			 
			Object resultObj = method.invoke(handler, paramValues);
			
			return resultObj;			
		}else {
			throw new HException("2015", "BaseRestServlet:method is null");
		}
	}
	/**
	 * 
	 * @param clazz
	 * @return
	 * @throws Exception 
	 * @author: wanghua
	 */
	public static ClassSimpleInfo getHandlerClassInfo(Class<?> clazz) throws Exception {
		if(clazz == null)return null;		
		ClassSimpleInfo classSimpleInfo= new ClassSimpleInfo(clazz.getSimpleName(), clazz.getName());
		List<String> pathNames = getMethodPath(clazz);
		for(String pathName : pathNames) {
			Method method = findMethod(clazz, pathName);
			if(method == null)continue;
			MethodSimpleInfo methodSimpleInfo = new MethodSimpleInfo(method.getName(), pathName);
			List<ArgumentSimpleInfo> params = getParams(method);
			for(ArgumentSimpleInfo param: params){
				methodSimpleInfo.addArgument(param);
			}
			

			Class<?> returnType = method.getReturnType();
			Type genericReturnType = method.getGenericReturnType();// 获取返回值类型
			ArgumentSimpleInfo returnArg = getParamInfo(returnType, genericReturnType);
			methodSimpleInfo.setResultDataType(returnArg.getDataType());
			methodSimpleInfo.setResultData(returnArg.getSampleData());
			
			classSimpleInfo.addMethodSimpleInfo(methodSimpleInfo);
		}		
		return classSimpleInfo;//TJSON.toJSONString(classSimpleInfo);
	}
	
	/**从clazz上查找@Path注解值为methodPathName的method
	 * Path可以注解在类方法，父类方法，或者接口函数上。
	 * 此方法会对method进行缓存，第二次开始，对同一个clazz和methodPathName，会从cache中取值
	 * @param clazz
	 * @param methodPathName
	 * @return 
	 * @author: wanghua
	 * @reviewer: 
	 */
	public static Method findMethod(Class<?> clazz, String methodPathName){
		if(clazz == null || clazz == void.class) {
			return null;
		}
		String cacheKey = clazz.getName()+".."+methodPathName;
		Object cacheObj = InMemoryCache.DEFAULT().get(CLASS_INFO_CACHE_NAME, cacheKey);
		if("NONE".equals(cacheObj)) return null;
		Method cachedMethod = (Method)cacheObj;
		if(cachedMethod == null) {
			synchronized (CLASS_INFO_CACHE_NAME) {
				Method method = doFindMethod(clazz, methodPathName);
				if(method != null) {
					cachedMethod = method;
					InMemoryCache.DEFAULT().put(CLASS_INFO_CACHE_NAME, cacheKey, cachedMethod);
				}else {
					InMemoryCache.DEFAULT().put(CLASS_INFO_CACHE_NAME, cacheKey, "NONE");
				}
			}
		}
		return cachedMethod;
	}
	
	private static Method doFindMethod(Class<?> clazz, String methodPathName){
		Method foundMethod = null;
		Method[] methods = clazz.getMethods();
		for(Method method : methods){
			Path path = method.getAnnotation(Path.class);	
			if(path != null){
				String pathName = path.value();
				if(pathName != null){
					pathName = pathName.replaceAll("/", "");
					if(pathName.equals(methodPathName)){						
						foundMethod = method;
						break;
					}
				}
			}
		}
		if(foundMethod != null)return foundMethod;
		else{
			Class<?> superclass = clazz.getSuperclass();
			if(superclass != null) {
				foundMethod = doFindMethod(superclass, methodPathName);
				if(foundMethod == null){
					Class<?>[] interfaces = clazz.getInterfaces();
					for(Class<?> interface1 : interfaces){
						foundMethod = doFindMethod(interface1, methodPathName);
						if(foundMethod != null) {
							return foundMethod;
						}
					}
				}else {
					return foundMethod;
				}
			}
		}
		return foundMethod;
	}
	
	
	/**返回method的参数信息（根据@Param注解定义的名称）
	 * @param method
	 * @return 
	 * @author: wanghua
	 * @reviewer: 
	 */
	public static List<ArgumentSimpleInfo> getParams(Method method){
		List<ArgumentSimpleInfo> params = new ArrayList<>();
		Parameter[] parameters = method.getParameters();
		for(Parameter parameter : parameters) {
			//String paramName = parameter.getName();
			String paramAnnoName = "";
			Param paramAnno = parameter.getAnnotation(Param.class);
			if(paramAnno != null) {
				paramAnnoName = paramAnno.value();
			}
			
			Class<?> paramType = parameter.getType();
			Type parameterizedType = parameter.getParameterizedType();
			ArgumentSimpleInfo arg = getParamInfo(paramType, parameterizedType);
			arg.setArgumentName(paramAnnoName);
			params.add(arg);
		}
		return params;
	}
	private static ArgumentSimpleInfo getParamInfo(Class<?> paramType, Type parameterizedType) {
		ArgumentSimpleInfo arg = new ArgumentSimpleInfo();
		String dataType = "";
		Object sampleData = null;
		if(paramType.isArray()) {
			dataType = parameterizedType.toString();// + "[]";
			if(parameterizedType instanceof ParameterizedType) {
				ParameterizedType pType = (ParameterizedType)parameterizedType;	
				//sampleData = new Object[] {pType.getActualTypeArguments()[0].getClass().newInstance()};
				sampleData = new Object[] {BeanSchema.parse(pType.getActualTypeArguments()[0].getClass())};
			}
		}else if(List.class.isAssignableFrom(paramType) ){
			dataType = parameterizedType.toString();
			if(parameterizedType instanceof ParameterizedType) {
				ParameterizedType pType = (ParameterizedType)parameterizedType;
				Type actualType = pType.getActualTypeArguments()[0];
				Class<?> actualClass = (Class<?>)actualType;
				//sampleData = new Object[] {actualClass.newInstance()};
				sampleData = new Object[] {BeanSchema.parse(actualClass)};
			}
		}else {
			dataType = paramType.getName();
			sampleData = new Object[] {BeanSchema.parse(paramType)};	
		}
		
		arg.setDataType(dataType);
		arg.setSampleData(sampleData);
		return arg;
	}
		
	/**获取class（包括父类，接口）上定义的Path注解值
	 * @param clazz
	 * @return 
	 * @author: wanghua
	 * @reviewer: 
	 */
	private static List<String> getMethodPath(Class<?> clazz){
		List<String> paths = new ArrayList<>();
		getMethodPaths(paths, clazz);
		return paths;
	}
	
	/**获取class（包括父类，接口）上定义的Path注解值
	 * @param paths
	 * @param clazz
	 * @author: wanghua
	 * @reviewer: 
	 */
	private static void getMethodPaths(List<String> paths, Class<?> clazz){
		if(clazz == null || clazz == void.class) {
			return;		
		}
		Method[] methods = clazz.getMethods();
		for(Method method : methods){
			Path path = method.getAnnotation(Path.class);	
			if(path != null){
				String pathName = path.value();
				if(pathName != null){
					//pathName = pathName.replaceAll("/", "");
					if(!paths.contains(pathName)){
						paths.add(pathName);
					}
				}
			}
		}
		Class<?> superclass = clazz.getSuperclass();			
		getMethodPaths(paths, superclass);
		 
		Class<?>[] interfaces = clazz.getInterfaces();
		for(Class<?> interface1 : interfaces){
			getMethodPaths(paths, interface1);					
		}
	}
	
}
